package dao;

import application.exceptions.ItemNotFoundException;
import mappings.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import utils.HibernateUtil;

import java.util.List;

/*
 * - Bardzo nieefektywne :P
 * - Dla 100 materialow utworzenie bazowego grida bedzie wymagalo 500 zapytan do bazy
 * - Brak lepszej opcji dla aktualnej schemy bazy danych i wymaganego pliku wyjsciowego
 */
@SuppressWarnings("JpaQlInspection")
public class MaterialsDefinitionDao
{
    private static SessionFactory sessionFactory = HibernateUtil.getSessionFactory();

    public List<MaterialDefinition> getAllMaterialDefinitionsForMaterial(int materialId)
    {
        Session session = sessionFactory.openSession();

        List<MaterialDefinition> materialDefinitions = session.createCriteria(MaterialDefinition.class)
                .add(Restrictions.eq("material.id", materialId))
                .list();

        session.close();

        return materialDefinitions;
    }

    /*
     * Wybiera wszystkie mozliwe pary material/standard
     */
    public List<Object[]> getDistinctMaterialDefinitions()
    {
        Session session = sessionFactory.openSession();

        ProjectionList projectionList = Projections.projectionList();
        projectionList.add(Projections.property("material"));
        projectionList.add(Projections.property("standards"));

        List<Object[]> materialDefinitions = session.createCriteria(MaterialDefinition.class)
                .setProjection(Projections.distinct(projectionList))
                .list();

        session.close();

        return materialDefinitions;
    }

    /*
     * Dla wybranej pary material/standard pobiera zadana wartosc feature'a (Rm, A5, HB...)
     */
    public Object[] getMaterialDefinitionWithFeature(Material material, Standards standard, String feautreName)
    {
        Session session = sessionFactory.openSession();

        ProjectionList projectionList = Projections.projectionList();
        projectionList.add(Projections.property("m.min"));
        projectionList.add(Projections.property("m.max"));

        Object[] materialDefinition = (Object[]) session.createCriteria(MaterialDefinition.class, "m")
                .createAlias("m.features", "f")
                .add(Restrictions.eq("m.material", material))
                .add(Restrictions.eq("m.standards", standard))
                .add(Restrictions.eq("f.name", feautreName))
                .setProjection(projectionList)
                .setMaxResults(1)
                .uniqueResult();

        session.close();

        if(materialDefinition == null)
        {
            return new Object[2];
        }

        return materialDefinition;
    }


    public MaterialDefinition addMaterialDefinition(int materialId, int standardId, int featureId, double min, double max) throws ItemNotFoundException
    {
        MaterialDefinition materialDefinition = new MaterialDefinition();

        org.hibernate.classic.Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        Material material = (Material) session.get(Material.class, materialId);
        Standards standard = (Standards) session.get(Standards.class, standardId);
        Features feature = (Features) session.get(Features.class, featureId);

        if(material == null || standard == null || feature == null)
        {
            tx.rollback();
            session.close();
            throw new ItemNotFoundException();
        }

        materialDefinition.setMaterial(material);
        materialDefinition.setStandards(standard);
        materialDefinition.setFeatures(feature);
        materialDefinition.setMin(min);
        materialDefinition.setMax(max);

        session.save(materialDefinition);

        tx.commit();
        session.close();

        return materialDefinition;
    }

    public MaterialDefinition editMaterialDefinition(Integer id, int materialId, int standardId, int featureId, double min, double max) throws ItemNotFoundException
    {
        org.hibernate.classic.Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        MaterialDefinition materialDefinitionToEdit = (MaterialDefinition) session.get(MaterialDefinition.class, id);
        Material material = (Material) session.get(Material.class, materialId);
        Standards standard = (Standards) session.get(Standards.class, standardId);
        Features feature = (Features) session.get(Features.class, featureId);

        if(materialDefinitionToEdit == null || material == null || standard == null || feature == null)
        {
            tx.rollback();
            session.close();
            throw new ItemNotFoundException();
        }

        materialDefinitionToEdit.setMaterial(material);
        materialDefinitionToEdit.setFeatures(feature);
        materialDefinitionToEdit.setStandards(standard);
        materialDefinitionToEdit.setMin(min);
        materialDefinitionToEdit.setMax(max);
        session.update(materialDefinitionToEdit);
        tx.commit();
        session.close();

        return materialDefinitionToEdit;
    }

    public void deleteMaterialDefinition(Integer id) throws ItemNotFoundException
    {
        org.hibernate.classic.Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        int affectedRows = session.createQuery("delete MaterialDefinition where id = :id")
                .setParameter("id", id)
                .executeUpdate();

        if(affectedRows == 0)
        {
            tx.rollback();
            session.close();
            throw new ItemNotFoundException();
        }

        tx.commit();
        session.close();
    }
}
